package com.github.huifer.encryption.bean;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;

import com.github.huifer.encryption.core.RSAEncrypt;
import com.github.huifer.encryption.ex.PrivateKeyFindError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;

public class EnableEncryptionData implements BeanFactoryPostProcessor, Ordered {

	public static final String PREFIX_PROPERTY = "encryption.prefix";

	public static final String SUFFIX_PROPERTY = "encryption.suffix";

	public static final String RSA_PUBLIC_KEY_PROPERTY = "encryption.rsa.publicKey";

	public static final String RSA_PRIVATE_KEY_PROPERTY = "encryption.rsa.privateKey";

	public static final String DEFAULT_PREFIX = "PWD[";

	public static final String DEFAULT_SUFFIX = "]";

	public static final String HF_PRIVATE_KEY_PATH = "hf_private_key";

	private static final Logger LOG = LoggerFactory.getLogger(EnableEncryptionData.class);

	private static final Properties properties = new Properties();

	private final ConfigurableEnvironment environment;

	private String prefix;

	private String suffix;

	private String privateKey;

	public EnableEncryptionData(ConfigurableEnvironment environment) {
		this.environment = environment;
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

		MutablePropertySources propertySources = environment.getPropertySources();
		for (PropertySource<?> propertySource : propertySources) {
			if (propertySource.getSource() instanceof Map
			) {
				Map<String, Object> source = (Map) propertySource.getSource();
				source.forEach((k, v) -> {
					String property = environment.getProperty(k);
					if (hasPreAndSuf(property)) {
						LOG.info("开始处理 k = [{}]", k);
						try {
							String relay = splitPreAndSuf(property, this.prefix, this.suffix);
							String decrypt = RSAEncrypt.decrypt(relay, getPrivateKey(environment));
							source.put(k, decrypt);
						}
						catch (Exception e) {
							LOG.error("e = ", e);
						}
					}
				});
			}
		}
	}

	private String getPrivateKey(ConfigurableEnvironment environment) throws PrivateKeyFindError {
		this.privateKey = System.getProperty(RSA_PRIVATE_KEY_PROPERTY, "");
		if (StringUtils.hasText(this.privateKey)) {
			return this.privateKey;
		}

		this.privateKey = environment.getProperty(RSA_PRIVATE_KEY_PROPERTY);
		if (StringUtils.hasText(this.privateKey)) {
			return this.privateKey;
		}


		this.privateKey = (String) properties.get(RSA_PRIVATE_KEY_PROPERTY);
		if (StringUtils.hasText(this.privateKey)) {
			return this.privateKey;
		}


		throw new PrivateKeyFindError("rsa private key is null!");
	}

	private boolean hasPreAndSuf(String property) {
		return property.startsWith(getPrefix(environment)) && property.endsWith(getSuffix(environment));
	}

	protected String splitPreAndSuf(String str, String prefix, String suffix) {
		return str.replace(prefix, "").replace(suffix, "");
	}

	private String getSuffix(ConfigurableEnvironment environment) {
		this.suffix = environment.getProperty(SUFFIX_PROPERTY);
		if (StringUtils.hasLength(suffix)) {
			return this.suffix;
		}
		this.suffix = DEFAULT_SUFFIX;
		return DEFAULT_SUFFIX;
	}

	private String getPrefix(ConfigurableEnvironment environment) {
		this.prefix = environment.getProperty(PREFIX_PROPERTY);
		if (StringUtils.hasLength(prefix)) {
			return this.prefix;
		}
		this.prefix = DEFAULT_PREFIX;
		return DEFAULT_PREFIX;
	}

	@Override
	public int getOrder() {
		return Ordered.LOWEST_PRECEDENCE - 100;
	}

	static {
		InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(HF_PRIVATE_KEY_PATH);
		try {
			if (resource != null) {
				properties.load(resource);
			}
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}
}